home *** CD-ROM | disk | FTP | other *** search
- /*
- File: TMP_Config.c
-
- Contains: The non-resident code for our configurator class
-
- This code is not intended to implement an actual
- working configurator. It comes with no warranty,
- express or implied, as to it's suitability for your
- own configuration implementation.
- */
-
- #ifndef __TMP_CONFIG__
- #include "TMP_Config.h"
- #endif
- #ifndef __OPENTPTLINKS__
- #include <OpenTptLinks.h>
- #endif
- #ifndef __OTSHAREDLIBS__
- #include "OTSharedLibs.h"
- #endif
- #ifndef __MISTREAM__
- #include <mistream.h>
- #endif
- #ifndef __TIHDR__
- #include <tihdr.h>
- #endif
-
- #ifndef __FILES__
- #include <Files.h>
- #endif
-
- #if TMPCONFIG_USES_ASLM
- #include <LibraryManagerUtilities.h>
- #else
- #include <CodeFragments.h>
- #endif
-
- /*******************************************************************************
- ** Forward declarations
- ********************************************************************************/
-
- OSStatus TMPCreateStream(TOTConfigurator*, OTConfiguration*, OTOpenFlags, OTNotifyProcPtr, void*);
- OSStatus TMPConfigure(TOTConfigurator*, OTConfiguration*);
- void TMPHandleSystemEvent(TOTConfigurator*, OTEventCode, OTResult, void*);
-
- static void TMPCloseUnusedConfigurations(TOTConfigurator*);
- static void DestroyAConfiguration(AConfiguration*, OSStatus, boolean_p);
-
- static Boolean InitAConfiguration(AConfiguration*);
- static void DeleteAConfiguration(AConfiguration*);
- static OTResult TMPRemoveInterface(OTPortRef, OTResult, boolean_p);
- static OSStatus ConfigureNetworkLayer(OTConfiguration*);
- static OSStatus ConfigureDefaultLink(OTConfiguration*);
- static void CloseAConfiguration(AConfiguration* aCfig);
- static pascal void CreateNetworkLayer(OTStateMachine*);
- static pascal void OpenAndPush(OTStateMachine*);
- static void RemoveInterface(OTPortRef, OTResult, Boolean doneDeal);
- static Boolean ProcessOpenInfo(OTLink* link);
-
-
- /*
- * This is our process proc we use for timers and system tasks to unload
- * configurations.
- */
- pascal void MyConfigProcessProc(void*);
-
- /*******************************************************************************
- ** Globals
- ********************************************************************************/
- /*
- * Our global variables
- */
- #if GENERATING68K
- MyGlobal gTMPGlobal;
- #else
- extern MyGlobal gTMPGlobal;
- #endif
- /*
- * Our configurator object, so that we can access it from our non-C++ state
- * machines functions
- */
- static TMPConfigurator* gConfigData = NULL;
- static TOTConfigurator* gConfigor = NULL;
- /*
- * The file spec for our library so that we can get resources
- * out of it - real handy for OTNotifyUser.
- */
- static FSSpec gMyFile;
- /*
- * Our list of protocols.
- */
- static char* gList[] = { kProtocolList, 0 };
- /*
- * Size of preference structures for our various protocol
- * preferences.
- */
- static UInt16 gPrefLens[] =
- {
- sizeof(Prot1Prefs),
- sizeof(Prot2Prefs),
- sizeof(Prot3Prefs)
- };
-
- /*******************************************************************************
- ** MyEventProc
- **
- ** This procedure handles events from our control streams
- **
- ** This is our own function, and nothing outside our module knows about it.
- **
- ** NOTE: For Mac OS 8, this routine is always called in the "master" controller
- ** library (the one that created the control streams).
- ********************************************************************************/
-
- pascal void MyEventProc(void* ref, OTEventCode code, OTResult, void* cookie);
-
- pascal void MyEventProc(void* ref, OTEventCode code, OTResult result, void* cookie)
- {
- #pragma unused(cookie, result)
- /*
- * The reverence is the AConfiguration that corresponds to the control stream
- * that is sending us the event. Remember, this is an event directly from a
- * stream, not from a provider, so the events are much lower-level events. This
- * code duplicates what the provider infrastructure does to process incoming
- * stream messages.
- */
- AConfiguration* aCfig = (AConfiguration*)ref;
- /*
- * If it's a SIGPOLL event, we want to read the event off of the
- * head of the stream.
- */
- if ( code == kSIGNALEVENT + SIGPOLL )
- {
- OTBuffer* buf;
- /*
- * We keep reading the streamhead until there's nothing on it!
- */
- while ( true )
- {
- /*
- * You could use "OTGetMessage" to get the message off of the stream,
- * or you can use our ReadMessage API
- */
- OTReadInfo rpBuf;
-
- rpBuf.fType = kOTAnyMsgType;
- rpBuf.fCommand = 0;
-
- buf = OTReadMessage(aCfig->fCtlStream, &rpBuf);
- /*
- * If there's nothing on the stream - get out of here
- */
- if ( buf == NULL )
- break;
-
- if ( (buf->fType != M_PROTO && buf->fType != M_PCPROTO) ||
- buf->fLen != sizeof(T_event_ind) ||
- ((T_event_ind*)buf->fData)->PRIM_type != T_EVENT_IND )
- {
- DebugStr("\pMyEventProc - unexpected buffer read");
- }
- else
- {
- /*
- * Let's notify the user about certain events. We have our file spec
- * stored in gMyFile, and we can provide the STR# resource ID and
- * index number (1-based, just like GetIndString). The other 2 parameters
- * can be used like ParamText, supplying strings to substitute into
- * ^0 and ^1 characters in the resource string.
- */
- switch ( ((T_event_ind*)buf->fData)->EVENT_code )
- {
- case kMyEvent1:
- OTNotifyUser(&gMyFile, kMyResourceID, kEvent1ID, NULL, NULL);
- break;
-
- case kMyEvent2:
- OTNotifyUser(&gMyFile, kMyResourceID, kEvent2ID, NULL, NULL);
- break;
-
- case kMyNoClientEvent:
- /*
- * Here, our control stream is telling us that all of our clients have
- * gone away and that we can schedule an unload
- */
- if ( OTCompareAndSwap8(kIsInUse, kIsScheduled, &aCfig->fStatus) )
- {
- /*
- * Schedule ourselves to unload after kMyUnloadTimeout milliseconds.
- * You can also schedule yourself to unload by just scheduling the
- * system task, and the unload will occur at the next System Task.
- * It is a horrible idea to unload yourself right now, because we're still
- * in the code that will be unloaded (and we're not at system task time).
- * However, you could call your "scheduleUnload" vector in the shared structure,
- * since that function will schedule a system task. We prefer to put in a
- * time delay here so that we don't thrash ourselves to death.
- */
- OTScheduleTimerTask(aCfig->fTimerTask, kMyUnloadTimeout);
- }
-
- }
- }
- /*
- * When we're all done, give the buffer back to Open Transport
- */
- OTReleaseBuffer(buf);
- }
- }
- else
- {
- DebugStr("\pMyEventProc - unexpected event");
- }
- }
-
- /*******************************************************************************
- ** AConfiguration routines
- ********************************************************************************/
-
- static Boolean InitAConfiguration(AConfiguration* aCfig)
- {
- if ( aCfig == NULL )
- return false;
- aCfig->fCtlStream = kOTInvalidStreamRef;
- aCfig->fHelperStream = kOTInvalidStreamRef;
- aCfig->fLinkID = kOTInvalidPortRef;
- aCfig->fOurID = kOTInvalidPortRef;
- aCfig->fStatus = kIsBeingCreated;
- aCfig->fPrefs = NULL;
- aCfig->fNetworkName[0] = 0;
- /*
- * This cannot be moved to the TMP_CreateConfig file, because we need the pointers
- * to the MyConfigProcessProc to be real pointers to real routines, not ASLM or CFM
- * stubs (otherwise we'll be calling through glue that goes "bye-bye" when the
- * CreateConfig library unloads.
- */
- aCfig->fTimerTask = OTCreateTimerTask(MyConfigProcessProc, aCfig);
- aCfig->fSystemTask = OTCreateSystemTask(MyConfigProcessProc, aCfig);
- return aCfig->fTimerTask != 0 && aCfig->fSystemTask != 0;
- }
-
- static void DeleteAConfiguration(AConfiguration* aCfig)
- {
- /*
- * These functions act correctly if the vars are 0, so let's just call
- * them rather than testing for 0
- */
- OTDestroyTimerTask(aCfig->fTimerTask);
- OTDestroySystemTask(aCfig->fSystemTask);
- OTFreeSharedClientMem(aCfig);
- }
-
- /*******************************************************************************
- ** MyConfigProcessProc
- **
- ** This is our own function, and nothing outside our module knows about it.
- ********************************************************************************/
-
- /*
- * A helpful routine to use when searching our lists to see if the link
- * passed in (which is the link of an "AConfiguration" object) matches
- * the portref passed in. We add the extra piece of logic that if the
- * ref is kOTInvalidPortRef, it matches all configurations.
- */
- static Boolean FindMyConfig(const void* ref, OTLink* link)
- {
- if ( OTGetLinkObject(link, AConfiguration, fMyLink)->fStatus == kIsUnused )
- return false;
- if ( *(const OTPortRef*)ref == kOTInvalidPortRef )
- return true;
- return OTGetLinkObject(link, AConfiguration, fMyLink)->fLinkID == *(const OTPortRef*)ref;
- }
- /*
- * This function finds any configuration that depends on the OTPortRef. In the prior
- * function, it must be exactly the OTPortRef.
- */
- static Boolean FindMyConfigByPort(const void* ref, OTLink* link)
- {
- if ( OTGetLinkObject(link, AConfiguration, fMyLink)->fStatus == kIsUnused )
- return false;
- if ( *(const OTPortRef*)ref == kOTInvalidPortRef )
- return true;
- return OTIsDependentPort(OTGetLinkObject(link, AConfiguration, fMyLink)->fLinkID,
- *(const OTPortRef*)ref);
- }
-
- pascal void MyConfigProcessProc(void* arg)
- {
- OTResult result = 0;
- AConfiguration* aCfig = (AConfiguration*)arg;
- /*
- * If our unload cancelled, just forget it!
- */
- if ( aCfig->fStatus != kIsScheduled )
- return;
- /*
- * If we're not at system task time, schedule a system task
- */
- if ( !OTCanMakeSyncCall() )
- {
- OTScheduleSystemTask(aCfig->fSystemTask);
- return;
- }
-
- /*
- * If we can't "enter the gate", reschedule for retry. We need to enter
- * the gate, because we're about to commit to unloading. If some interrupt
- * code should decide to asynchronously open an endpoint, we want it to wait
- * on the queue until we're all done tearing this down, just in case it's a
- * request for the same link (We have to commit sometime, and we can always
- * run into this situation).
- */
- while ( !OTEnterGate(&gConfigData->fGate, NULL) )
- {
- if ( !OTLeaveGate(&gConfigorData->fGate )
- {
- OTScheduleTimerTask(aCfig->fTimerTask, kMyUnloadTimeout);
- return;
- }
- }
- /*
- * If we're still schedule to unload, we're now committed
- */
- if ( aCfig->fStatus == kIsScheduled )
- {
- /*
- * Remove ourself from the list of configurations, and destroy
- * the configuration.
- */
- (void)OTRemoveLink(&gConfigData->fGlobal->fConfigs, &aCfig->fMyLink);
- DestroyAConfiguration(aCfig, kOTNoError, true);
- /*
- * If there are no more configurations, and there are no outstanding
- * open requests, we can shut down
- */
- if ( gConfigData->fGlobal->fConfigs.fHead == NULL && gConfigData->fList == NULL )
- {
- OTLeaveGate(&gConfigData->fGate);
- TMPCloseUnusedConfigurations(gConfigor);
- return;
- }
- }
- OTLeaveGate(&gConfigData->fGate);
- }
-
- /*******************************************************************************
- ** OTCreateMyConfigurator
- **
- ** This is the function our resident guy calls to create the configurator.
- **
- ** This function is known only to our resident code.
- ********************************************************************************/
-
- static void OTInitMyConfigurator(TMPConfigurator* cfig)
- {
- cfig->fGlobal = &gTMPGlobal;
- cfig->fShared = NULL;
- cfig->fList = NULL;
- cfig->fShuttingDown = false;
- cfig->fDontUnload = false;
- OTInitGate(&cfig->fGate, ProcessOpenInfo);
- }
-
- OSStatus OTCreateMyConfigurator(MySharedStruct* info)
- {
- OSStatus err = kOTNoError;
- TMPConfigurator* data = (TMPConfigurator*)OTAllocSharedClientMem(sizeof(TMPConfigurator*));
- TOTConfigurator* cfig;
-
- if ( data == NULL )
- return kENOMEMErr;
-
- OTInitMyConfigurator(data);
-
- cfig = OTNewConfigurator(data, TMPConfigure, TMPCreateStream, TMPHandleSystemEvent);
- if ( cfig == NULL )
- {
- OTFreeSharedClientMem(data);
- return kENOMEMErr;
- }
- /*
- * Create a system task for us to go through our teardown logic.
- */
- info->systemTask = OTCreateSystemTask(info->unloadProc, NULL);
- /*
- * If everything's cool, store our configurator off into our global,
- * and copy it into the shared structure's "cfig" field. Also,
- * keep a copy of the fShared structure so that we can reference
- * the unload vector.
- * Otherwise, delete the configurator and return an error.
- */
- if ( info->systemTask == 0 )
- {
- OTFreeSharedClientMem(data);
- OTDeleteConfigurator(cfig);
- return kENOMEMErr;
- }
- gConfigor = cfig;
- gConfigData = data;
- /*
- * Give the resident guy a pointer to our configurator. It doesn't really
- * need it, but you never know.
- */
- info->cfig = cfig;
- /*
- * Pick up the shared structure pointer.
- */
- data->fShared = info;
-
- return kOTNoError;
- }
-
- /*******************************************************************************
- ** ProcessATOpenInfo
- **
- ** This routine is called by the gate code whenever we need to process
- ** a DDP creation
- ********************************************************************************/
-
- void CreateStreamDoneProc(void* unUsed)
- {
- #pragma unused (unUsed)
-
- OTLeaveGate(&gConfigData->fGate);
- }
-
- static Boolean ProcessOpenInfo(OTLink* link)
- {
- TMPOpenInfo* info = OTGetLinkObject(link, TMPOpenInfo, fLink);
- if ( gConfigData->fShuttingDown )
- {
- info->fStateMachine->fResult = kENXIOErr;
- OTSMPopCallback(info->fStateMachine);
- OTSMComplete(info->fStateMachine);
- return true;
- }
- OTSMInstallCompletionProc(info->fStateMachine, CreateStreamDoneProc, NULL);
- if ( info->fToPush == NULL )
- OTSMCallStateProc(info->fStateMachine, CreateNetworkLayer, 0);
- else
- OTSMCallStateProc(info->fStateMachine, OpenAndPush, 0);
- /*
- * We must return false even if we're already done, because the state machine
- * will call our completion proc, which will call LeaveGate(). If we return
- * true, we'll be inside the gate executing twice, which would be
- * embarrassing.
- */
- return false;
- }
-
- /*******************************************************************************
- ** HandleSystemEvent
- **
- ** Called by the system when something notable occurs
- ********************************************************************************/
-
- void TMPHandleSystemEvent(TOTConfigurator* cfig, OTEventCode event, OTResult result,
- void* cookie)
- {
- OTLink* link;
- /*
- * Of course, we can also use our global variables here - it had better be the same!
- */
- TMPConfigurator* data = (TMPConfigurator*)OTGetConfiguratorUserData(cfig);
-
- switch ( event )
- {
- case kOTSystemSleep:
- /*
- * For sleep, we've adopted the same policy as shutdown for AppleTalk and
- * TCP/IP just to keep things simple. AppleTalk has to because it can't
- * defend it's node address while it is asleep. If TCP/IP is not using
- * MacIP, it could actually stay alive, but for now.....
- */
- case kOTSystemShutdown:
- {
- /*
- * Tell ourselves that we're shutting down
- */
- data->fShuttingDown = true;
- /*
- * Lock the gate, in case we're not already inside it, so that no more
- * opens start up
- */
- OTEnterGate(&data->fGate, NULL);
- /*
- * Now destroy all the configurations
- */
-
- while ( (link = OTRemoveFirst(&data->fGlobal->fConfigs)) != NULL )
- {
- AConfiguration* aCfig = OTGetLinkObject(link, AConfiguration, fMyLink);
- DestroyAConfiguration(aCfig, (OTResult)event, false);
- }
- OTLeaveGate(&data->fGate);
- data->fShuttingDown = false;
- break;
- }
-
- case kOTPortDisabled:
- /*
- * If a port has been disabled, call our TMPRemoveInterface function so that
- * if we depend on the disabled port, we blow the configuration away.
- * NOTE: The cookie for this event is the OTPortRef that's gone. We
- * Set "doneDeal" to true because typically, the port is history by the
- * time we get this notification.
- */
- TMPRemoveInterface((OTPortRef)cookie, result, true);
- break;
- }
- }
-
- /*******************************************************************************
- ** ConfigureDefaultLink
- **
- ** You probably don't need this method if you're configuring a link layer, but it's
- ** a placehold for protocols that might have the concept of a "default link" in
- ** their prefs somewhere. AppleTalk currently uses pRAM. TCP/IP currently
- ** does not allow anything but a default link, and it's retrieved from the
- ** preferences stored in the preferences file.
- **
- ** This is our own function, and nothing outside our module knows about it.
- ********************************************************************************/
-
- OSStatus ConfigureDefaultLink(OTConfiguration* cfig)
- {
- OSStatus err;
- OTPortRecord record;
- /*
- * In this routine, you would determine what you're default link should be,
- * push it as a child, and return OTConfigureChildren. As an example,
- * we try to find a motherboard ethernet device and use it.
- */
- if ( OTFindPortByRef(&record, OTCreatePortRef(kOTMotherboardBus, kOTEthernetDevice, 0, 0)) )
- {
- if ( OTCfigPushNewSingleChild(cfig, record.fPortName, &err) == NULL )
- return err;
- return OTConfigureChildren(cfig);
- }
- return kOTNotSupportedErr;
- }
-
- /*******************************************************************************
- ** ConfigureNetworkLayer
- **
- ** You probably don't need this method if you're configuring a link layer. It's
- ** a placeholder because often, the network layer of a protocol is really a
- ** streams driver rather than a pushable module, so we need a single copy
- ** of the network layer that we can clone for clients (see TMP_CreateConfig.cp
- ** for how this is actually done).
- **
- ** This is our own function, and nothing outside our module knows about it.
- ********************************************************************************/
-
- static OSStatus ConfigureNetworkLayer(OTConfiguration* cfig)
- {
- OSStatus err;
- OTConfiguration* kid;
- /*
- * Our network layer can only have 1 thing underneath it.
- */
- if ( OTCfigGetChild(cfig, 1) != NULL )
- return kOTNotSupportedErr;
-
- kid = OTCfigGetChild(cfig, 0);
- /*
- * If we have no child, configure the default link
- */
- if ( kid == NULL )
- return ConfigureDefaultLink(cfig);
- /*
- * Configure our children so we can see what's really going on.
- */
- err = OTConfigureChildren(cfig);
-
- if ( err != kOTNoError )
- return err;
- /*
- * Just in case our child changed, pick it up again.
- */
- kid = OTCfigGetChild(cfig, 0);
- /*
- * If the kid's not a port - bummer
- */
- if ( !OTCfigIsPort(kid) )
- return kOTNotSupportedErr;
- /*
- * Let's see if it's a port we don't support. This code is the way AppleTalk
- * used to determine if the port is usable or not. Your mileage may vary.
- */
- switch ( OTGetDeviceTypeFromPortRef(OTCfigGetPortRef(kid)) )
- {
- /*
- * Devices we know we don't support
- */
- case kOTSerialDevice:
- case kOTModemDevice:
- case kOTISDNDevice:
- case kOTATMDevice:
- case kOTATMSNAPDevice:
- case kOTMDEVDevice:
- err = kOTNotSupportedErr;
- break;
- /*
- * Devices that have special logic
- */
- case kOTEthernetDevice:
- case kOTFastEthernetDevice:
- {
- /*
- * For ethernet - make sure the port supports 8022 framing
- */
- OTPortRecord record;
- if ( !OTFindPort(&record, OTCfigGetProviderName(kid)) || !(record.fCapabilities & kOTFraming8022) )
- err = kOTNotSupportedErr;
- break;
- }
- /*
- * We'll assume we can work with any other DLPI driver. This may not be true,
- * but we're willing to give it a shot. In OT 1.5, AppleTalk will look for
- * a configuration library that will contain some functions to help us
- * configure over unfamiliar links.
- */
- default:
- if ( !(OTCfigGetInstallFlags(kid) & kOTPortIsDLPI) )
- err = kOTNotSupportedErr;
- break;
- }
- return err;
- }
-
- /*******************************************************************************
- ** Configure
- **
- ** This routine is used to specify the configuration of our object. It is called
- ** by the Open Transport infrastructure when a client opens a provider that your
- ** "CanConfigure" function returned true for.
- ********************************************************************************/
-
- OSStatus TMPConfigure(TOTConfigurator* cfigor, OTConfiguration* cfig)
- {
- #pragma unused(cfigor)
-
- const char* name = OTCfigGetProviderName(cfig);
- int index = -1;
- OSStatus err;
- OTConfiguration* kid;
-
- /*
- * Find which layer we're configuring. This call cannot fail unless
- * someone manually calls us, since we know that "CanConfigure" passed.
- */
- while ( gList[++index] != NULL )
- {
- if ( OTStrEqual(name, gList[index]) )
- break;
- }
- /*
- * Now, do some sanity checking.
- */
- kid = OTCfigGetChild(cfig, 0);
- /*
- * Obviously, modify this test if not appropriate. For links, usually
- * the test is for "kid" being NULL.
- */
- if ( OTCfigGetChild(cfig, 1) != NULL )
- {
- return kENXIOErr;
- }
- /*
- * If you're a link, you don't need this. For a protocol, let's assume
- * the kProt1 is your network layer and kProt2 and kProt3 are protocols you
- * could push on top
- */
- switch ( index )
- {
- default:
- return kENXIOErr;
-
- case kProt1ID:
- return ConfigureNetworkLayer(cfig);
-
- case kProt2ID:
- case kProt3ID:
- /*
- * If we don't have a child, or it's not our network layer,
- * push a copy of our network layer as our child, then
- * call OTConfigureChildren to complete the configuration.
- */
- if ( kid == NULL || !OTStrEqual(OTCfigGetProviderName(kid), gList[kProt1ID]) )
- {
- /*
- * "Push" a copy of our protocol1 underneath us as a child.
- * If it returns NULL, an error occured.
- */
- if ( OTCfigPushNewSingleChild(cfig, gList[kProt1ID], &err) == NULL )
- return err;
- }
- /*
- * Configure our children. This will recursively call us to
- * configure our network layer.
- */
- return OTConfigureChildren(cfig);
-
- }
- }
-
- /*******************************************************************************
- ** OpenAndPush
- **
- ** This procedure is used for asynchronous opens, where we have to Open the module
- ** below us, then push a module on top of it.
- **
- ** Using the OTStateMachine object allows us to asynchronously do common
- ** stream functions in a way that looks pseudo-synchronous. Each of these
- ** functions returns a Boolean that's true if the function is already complete, and
- ** false if the function is not yet complete. If the function is not yet complete,
- ** just return (but have the "next" state set up before calling the function or
- ** bad things could happen). The state machine will call back in to your function
- ** when it finally completes. If it is complete, typically a continue is executed
- ** to reenter the switch statement. In all cases, the fResult field will have
- ** the result value from the function. DO NOT FORGET that IOCTL functions can
- ** return positive values that are not errors, so be sure to test for "less than"
- ** 0 if the function you exectued was an IOCTL.
- **
- ** This is our own function, and nothing outside our module knows about it.
- ********************************************************************************/
-
- static pascal void OpenAndPush(OTStateMachine* sm)
- {
- TMPOpenInfo* info = (TMPOpenInfo*)OTSMGetClientData(sm);
-
- while ( true )
- {
- switch ( OTSMGetState(sm) )
- {
- case 0:
- {
- OTSMSetState(sm, 1);
- /*
- * If it's our own network layer we're opening, invoke our CreateNetworkLayer
- * state machine function. Otherwise, let's call CreateStream.
- */
- if ( OTStrEqual(OTCfigGetProviderName(info->fCfig), gList[kProt1ID]) )
- {
- /*
- * This code is really for Mac OS 8, where the control stream has to be
- * created in the master configurator. If we're the master configurator,
- * we just invoke our state machine to do the plumbing. If we're not,
- * we use the OTSMCreateControlStream function to get the data to our
- * master configurator to do the plumbing.
- */
- if ( OTIsMasterConfigurator(gConfigor) )
- {
- if ( !OTSMCallStateProc(sm, CreateNetworkLayer, 0))
- return;
- }
- else
- {
- if ( !OTSMCreateControlStream(sm, info->fCfig, gConfigor) )
- return;
- }
- }
- else
- {
- if ( !OTSMCreateStream(sm, info->fCfig, info->fOpenFlags) )
- return;
- }
- /*
- * If we fall through, then the routine we called finished synchronously, so we can
- * go on to the next state.
- */
- continue;
- }
-
- case 1:
- {
- OTSMSetState(sm, 2);
- /*
- * If an error occurred opening the stream, break and complete
- * to the client. Notification to the client will consist of the
- * "fCode", "fResult", and "fCookie" fields.
- */
- if ( sm->fResult != kOTNoError )
- {
- sm->fCode = kStreamOpenEvent;
- sm->fCookie = 0;
- break;
- }
- /*
- * Save the stream we're working on.
- */
- info->fTheStream = (StreamRef)sm->fCookie;
- /*
- * Invoke the IOCTL to push the module we're supposed to push
- */
- if ( !OTSMIoctl(sm, info->fTheStream, I_PUSH, (long) OTCfigGetProviderName(info->fToPush)) )
- return;
- /*
- * If we fall through, then the routine we called finished synchronously, so we can
- * go on to the next state.
- */
- continue;
- }
-
- case 2:
- {
- OTSMSetState(sm, 3);
- /*
- * Remember, it was an IOCTL, so fResult could be a positive number.
- */
- if ( sm->fResult >= 0 )
- {
- /*
- * Set up the code, result, and cookie to complete to the client.
- */
- sm->fCode = kStreamOpenEvent;
- sm->fCookie = info->fTheStream;
- sm->fResult = kOTNoError;
- break;
- }
- /*
- * An error occurred - close the original stream
- */
- OTStreamClose(info->fTheStream);
- break;
- }
- }
- break;
- }
- /*
- * Complete to the original client
- */
- OTSMPopCallback(info->fStateMachine);
- OTSMComplete(info->fStateMachine);
- }
-
- /*******************************************************************************
- ** CreateStream
- **
- ** This function must create the stream specified in the OTConfiguration. It is
- ** called when a client creates a provider, your "CanConfigure" function returned
- ** true for this configuration, and your "Configure" function return kOTNoError
- ** for the configuration, after modifying it to suit your taste.
- ********************************************************************************/
-
- OSStatus TMPCreateStream(TOTConfigurator* cfigor,
- OTConfiguration* cfig, OTOpenFlags flags,
- OTNotifyProcPtr proc, void* contextPtr)
- {
- TMPConfigurator* data = (TMPConfigurator*)OTGetConfiguratorUserData(cfigor);
- const char* name = OTCfigGetProviderName(cfig);
- TMPOpenInfo* info;
- OTStateMachine* sm;
- int index = -1;
- /*
- * We anticipate have 2 levels of state machine invocation (i.e. one state
- * machine invoking another). That's what the kOTSMBufferSize(2) is for.
- * If you give this too small a number, you'll trounce memory if you invoke
- * state machines too deeply.
- */
- char stakInfo[sizeof(TMPOpenInfo) + kOTSMBufferSize(2)];
- /*
- * If we're shutting down, return an immediate error
- */
- if ( data->fShuttingDown )
- return kENXIOErr;
-
- /*
- * Find which layer we're configuring. This routine is also never called without having
- * gone through "CanConfigure" and "Configure", so we know this is an OTConfiguration we
- * can live with, and that the top layer points to one of our protocols.
- */
- while ( gList[++index] != NULL )
- {
- if ( OTStrEqual(name, gList[index]) )
- break;
- }
-
- /*
- * We create a stream state machine. It will be created using the stack buffer for
- * synchronous calls, and the memory will be allocated for asynchronous calls.
- */
- sm = OTCreateStateMachine(stakInfo, sizeof(stakInfo), sizeof(TMPOpenInfo),
- proc, contextPtr);
-
- if ( sm == NULL )
- return kENOMEMErr;
-
- info = (TMPOpenInfo*)OTSMGetClientData(sm);
- /*
- * Save the open flags. STREAMS doesn't really use them, but you never know.....
- */
- info->fOpenFlags = flags;
- info->fStateMachine = sm;
- /*
- * If the index is not kProt1ID, set the fToPush configuration
- */
- if ( index != kProt1ID )
- {
- OTConfiguration* kid = OTCfigGetChild(cfig, 0);
- if ( kid == NULL )
- return kENXIOErr;
- info->fCfig = kid;
- info->fToPush = cfig;
- }
- /*
- * Enter the gate to execute the code. The gate will only execute one open
- * at a time and keep everything synchronized nicely.
- */
- OTEnterGate(&gConfigData->fGate, &info->fLink);
- /*
- * Always call "OTSMReturnToCaller" here. If the original call was synchronous,
- * OTSMReturnToCaller will wait for completion. Otherwise, it will set up
- * everything necessary to call back the original client when things do
- * complete.
- */
- return OTSMReturnToCaller(sm);
- }
-
- /*******************************************************************************
- ** CloseAConfiguration
- ** This function just closes down all the streams belonging to the configuration.
- **
- ** This is our own function, and nothing outside our module knows about it.
- ********************************************************************************/
-
- void CloseAConfiguration(AConfiguration* aCfig)
- {
- //
- // We set non-blocking mode because we want the close to happen right away
- // without waiting for queues to drain.
- //
- if ( aCfig->fHelperStream)
- {
- OTStreamSetNonBlocking(aCfig->fHelperStream);
- OTStreamClose(aCfig->fHelperStream);
- aCfig->fHelperStream = NULL;
- }
- if ( aCfig->fCtlStream)
- {
- OTStreamRemoveNotifier(aCfig->fCtlStream);
- OTStreamSetNonBlocking(aCfig->fCtlStream);
- OTStreamClose(aCfig->fCtlStream);
- aCfig->fCtlStream = NULL;
- }
- }
-
- /*******************************************************************************
- ** DestroyAConfiguration
- ** This function destroys the given configuration, telling all client's of this
- ** configuration that they're toast.
- **
- ** This is our own function, and nothing outside our module knows about it.
- ********************************************************************************/
-
- static void DestroyAConfiguration(AConfiguration* aCfig, OTResult why, boolean_p doneDeal)
- {
- aCfig->fStatus = kIsUnused;
- /*
- * Destroy our tasks so they don't fire inconveniently
- */
- OTDestroyTimerTask(aCfig->fTimerTask);
- OTDestroySystemTask(aCfig->fSystemTask);
- aCfig->fTimerTask = 0;
- aCfig->fSystemTask = 0;
- /*
- * For link configurators, you typically will call OTCloseMatchingProviders
- * with a 0 for the controlMask:
- *
- * OTCloseMatchingProviders(0, aCfig->fLinkID, why, doneDeal).
- */
- OTCloseMatchingProviders(gConfigData->fGlobal->fMyMask, aCfig->fLinkID, why, doneDeal);
- /*
- * Close down all our control streams
- */
- CloseAConfiguration(aCfig);
- OTFreeSharedClientMem( aCfig );
- }
-
- /*******************************************************************************
- ** CreateNetworkLayer
- **
- ** This is our state machine function to create our network layer. It determines
- ** if we already have a configuration, and also insures that we don't try to
- ** open the network layer reentrantly
- ********************************************************************************/
-
- typedef Boolean (*CreateNewConfigProcPtr)(TOTConfigurator*, OTStateMachine*);
-
-
- pascal void CreateNetworkLayer(OTStateMachine* sm)
- {
- TMPOpenInfo* info = (TMPOpenInfo*)OTSMGetClientData(sm);
-
- while ( true )
- {
- switch ( OTSMGetState(sm) )
- {
- case 0:
- {
- AConfiguration* aCfig;
- OTConfiguration* kid;
- OTLink* link;
-
- OTSMSetState(sm, 1);
- /*
- * Set up info for failure
- */
- sm->fCookie = (void*)kOTInvalidRef;
- sm->fCode = kStreamOpenEvent;
- /*
- * If there is no child - there is something wrong!
- */
- kid = OTCfigGetChild(info->fCfig, 0);
- if ( kid == NULL )
- {
- sm->fResult = kENXIOErr;
- OTSMPopCallback(info->fStateMachine);
- OTSMComplete(sm);
- return;
- }
- /*
- * Let's see if we can find a configuration that already exists. We use the
- * store fLinkID in the child to match configurations. You may need a more
- * complex test.
- */
- link = NULL;
- {
- for ( link = gConfigData->fGlobal->fConfigs.fHead; link != NULL; link = link->fNext )
- {
- aCfig = OTGetLinkObject(link, AConfiguration, fMyLink);
- if ( OTCfigGetPortRef(kid) == aCfig->fLinkID )
- {
- break;
- }
- }
- }
- /*
- * If we didn't find a configuration, or the configuration is
- * currently unset, reenter the state machine at state 2,
- * where we create the configuration.
- */
- if ( link == NULL )
- {
- info->fConfig = NULL;
- OTSMSetState(sm, 2);
- continue;
- }
- /*
- * Here, we have a configuration. Deal with it appropriately
- */
- info->fConfig = aCfig;
-
- switch ( aCfig->fStatus )
- {
- /*
- * If it's unused - we go to state 2 to re-plumb it
- */
- case kIsUnused:
- OTSMSetState(sm, 2);
- continue;
- /*
- * If it's scheduled, set the state to kIsInUse, remove the configuration
- * from the scheduler if it's there, and just go on to clone it.
- */
- case kIsScheduled:
- aCfig->fStatus = kIsInUse;
- OTCancelTimerTask(aCfig->fTimerTask);
- OTCancelSystemTask(aCfig->fSystemTask);
- break;
-
- case kIsInUse:
- break;
- }
- /*
- * Just clone our network layer using the name we stored of in "fNetworkName"
- */
- if ( !OTSMOpenStream(sm, aCfig->fNetworkName, 0) )
- return;
- continue;
- }
-
- case 1:
- {
- /*
- * Set up completing to the client with the current fResult value.
- */
- OTSMPopCallback(info->fStateMachine);
- OTSMComplete(sm);
- return;
- }
-
- /* -------------------------------------------------------------------------
- Here, we have either an unused configuration, or no configuration
- ------------------------------------------------------------------------- */
-
- case 2:
- {
- AConfiguration* aCfig = info->fConfig;
- OTConfiguration* kid = OTCfigGetChild(info->fCfig, 0);
- #if TMPCONFIG_USES_ASLM
- TLibraryManager* temp;
- #endif
-
- OTSMSetState(sm, 3);
- /*
- * If we don't have a configuration, we're going to have to create one.
- * Since creating one is in another library in this implementation, we
- * have to be able to load libraries, or we can't go one.
- */
- if ( aCfig == NULL )
- {
- ProcPtr proc;
- #if TMPCONFIG_USES_ASLM
- OSErr osErr;
- #endif
- /*
- * If we can't load libraries, conplete with an EAGAIN error to the client.
- */
- if ( !OTCanLoadLibraries() )
- {
- sm->fResult = kEAGAINErr;
- OTSMPopCallback(info->fStateMachine);
- OTSMComplete(sm);
- return;
- }
- /*
- * Load the library, get our function pointer to create the configuration, and
- * call it.
- */
- #if TMPCONFIG_USES_ASLM
- temp = SetSelfAsClient();
-
- if ( OTLoadASLMLibrary(kTMPCreateConfigASLMID) != noErr )
- {
- sm->fResult = kENXIOErr;
- OTSMSetState(sm, 4);
- continue;
- }
- proc = (ProcPtr)GetFunctionPointer(kTMPCreateConfigASLMID,
- "OTCreateMyConfiguration", &osErr);
- if ( proc == 0 )
- {
- OTUnloadASLMLibrary(kTMPCreateConfigASLMID);
- sm->fResult = kENXIOErr;
- OTSMSetState(sm, 4);
- continue;
- }
- #else
- proc = (ProcPtr)OTGetCFMPointer(kTMPCreateConfigCFMID, "OTCreateMyConfiguration",
- &info->fCreateID, kOTLoadACopy | kOTGetCodeSymbol);
- if ( proc == 0 )
- {
- sm->fResult = kENXIOErr;
- OTSMSetState(sm, 4);
- continue;
- }
- #endif
- info->fConfig = (AConfiguration*)OTAllocSharedClientMem(sizeof(AConfiguration));
- if ( !InitAConfiguration(info->fConfig) )
- {
- sm->fResult = kENOMEMErr;
- OTSMSetState(sm, 4);
- continue;
- }
-
- if ( !(*(CreateNewConfigProcPtr)proc)(gConfigor, sm) )
- return;
- continue;
- }
- }
-
- case 3:
- {
- OTSMSetState(sm, 4);
- /*
- * The result of our call to OTCreateMyConfiguration is now known, so let's unload
- * the library that did the creating of the configuration.
- */
- #if TMPCONFIG_USES_ASLM
- OTUnloadASLMLibrary(kTMPCreateConfigASLMID);
- #else
- OTReleaseCFMConnection(&info->fCreateID);
- #endif
- continue;
- }
-
- case 4:
- {
- /*
- * Now, let's deal with the result of creating the configuration
- */
- sm->fCode = kStreamOpenEvent;
- sm->fCookie = kOTInvalidRef;
- /*
- * If an error occured, complete this guy, run our queue, then call
- * CheckUseCounts so that we unload if there's nothing to do.
- */
- if ( sm->fResult != kOTNoError )
- {
- OTSMPopCallback(info->fStateMachine);
- OTSMComplete(sm);
- return;
- }
- /*
- * Now that we've created the configuration, let's go back around again and
- * create the network layer stream using the created configuration. This is
- * pretty rude, but it keeps the code smaller.
- */
- OTSMSetState(sm, 0);
- continue;
- }
- }
- }
- }
-
- /*******************************************************************************
- ** RemoveInterface
- **
- ** This method is an API that allows the outside world to remove an interface.
- ** When Open Transport closes AppleTalk, this is the function that it calls.
- ********************************************************************************/
-
- static OTResult TMPRemoveInterface(OTPortRef ref, OTResult why, boolean_p doneDeal)
- {
- OTLink* link;
- /*
- * We must be able to acquire the lock to remove the interface. Otherwise, we
- * could have ugly race conditions. Since this call is normally done by some type
- * of control panel at System Task time, this is not normally an issue.
- */
- while ( !OTEnterGate(&gConfigData->fGate, NULL) )
- {
- if ( !OTLeaveGate(&gConfigData->fGate) )
- return kEAGAINErr;
- }
- /*
- * If we can't make a synchronous call, then when we tell clients that they're providers
- * are gone, they can't make sync calls either, so we modify doneDeal to true so the
- * clients know that.
- */
- if ( !OTCanMakeSyncCall() )
- doneDeal = true;
- /*
- * We go in a loop, because "ref" could be kOTInvalidPortRef, which asks us to
- * remove all of our interfaces.
- */
- while ( (link = OTFindAndRemoveLink(&gConfigData->fGlobal->fConfigs, FindMyConfigByPort, &ref)) != NULL )
- {
- AConfiguration* aCfig = OTGetLinkObject(link, AConfiguration, fMyLink);
- DestroyAConfiguration(aCfig, why, doneDeal);
- }
- /*
- * Since we might have destroyed all our configurations, if we've got nothing to do,
- * and the fDontUnload flag is not set, schedule our unload.
- */
- if ( gConfigData->fGlobal->fConfigs.fHead == NULL && gConfigData->fList == NULL )
- {
- /*
- * If we're supposed to ignore this call right now, do so.
- */
- if ( !gConfigData->fDontUnload )
- {
- /*
- * Tell the OT Infrastructure that your configurator object is now history
- */
- OTConfiguratorUnloaded(gConfigor);
- /*
- * Do the callback to schedule the unload process
- */
- (*gConfigData->fShared->scheduleUnloadProc)();
- OTDeleteConfigurator(gConfigor);
- gConfigor = NULL;
- gConfigData = NULL;
- }
- }
- /*
- * Release the lock, and run any new open requests that may have come in.
- */
- OTLeaveGate(&gConfigData->fGate);
- return kOTNoError;
- }
-
- /*******************************************************************************
- ** TMPCloseUnusedConfigurations
- ********************************************************************************/
-
- static void TMPCloseUnusedConfigurations(TOTConfigurator* cfigor)
- {
- TMPConfigurator* cf = (TMPConfigurator*)OTGetConfiguratorUserData(cfigor);
-
- /* -------------------------------------------------------------------------
- If there's nothing left - delete myself and unload from memory.
- ------------------------------------------------------------------------- */
-
- if ( cf->fGlobal->fConfigs.fHead == NULL && cf->fList == NULL )
- {
- /*
- * If we're supposed to ignore this call right now, do so.
- */
- if ( cf->fDontUnload )
- return;
- /*
- * Tell the OT Infrastructure that your configurator object is now history
- */
- OTConfiguratorUnloaded(cfigor);
- /*
- * Do the callback to schedule the unload process
- */
- (*cf->fShared->scheduleUnloadProc)();
- OTDeleteConfigurator(cfigor);
-
- return;
- }
- }
-
- /*******************************************************************************
- ** Initialization/Termination routines
- ********************************************************************************/
-
- void TeardownTMPConfig()
- {
- CloseOpenTransport();
- }
-
- #if GENERATING68K
- #pragma segment A5Init
- #endif
-
- #if TMPCONFIG_USES_CFM
-
- pascal OSErr InitTMPConfig(CFragInitBlock* ibp)
- {
- OSStatus err;
-
- /*
- * Create our OTFileSpec so we know where our resources are located
- */
- gMyFile = *ibp->fragLocator.u.onDisk.fileSpec;
- /*
- * Call InitOpenTransport so that Open Transport does not unload out from
- * underneath us.
- */
- if ( ( err = InitOpenTransport()) != kOTNoError )
- {
- return err;
- }
- return noErr;
- }
-
- #else
-
- void InitTMPConfig(void)
- {
- OSStatus err;
-
- /*
- * Create our OTFileSpec so we know where our resources are located
- */
- {
- TMacFileSpec* spec = (TMacFileSpec*)GetFileSpec(GetLocalLibraryFile());
- gMyFile.vRefNum = spec->fVRefNum;
- gMyFile.parID = spec->fParID;
- gMyFile.name[0] = spec->fName[0];
- OTMemcpy(gMyFile.name + 1, spec->fName + 1, spec->fName[0]);
- }
- /*
- * Call InitOpenTransport so that Open Transport does not unload out from
- * underneath us.
- */
- if ( ( err = InitOpenTransport()) != kOTNoError )
- {
- Fail(err, nil);
- }
- }
-
- #endif
-
- #if GENERATING68K
- #pragma segment Main
- #endif
-